package pso.gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.Executors;

import javax.swing.AbstractAction;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.SortOrder;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.jdesktop.swingx.JXTable;

import pso.gui.slaves.PSOTypeCellEditor;
import pso.gui.slaves.SlavesTableModel;
import pso.implementation.PSO;
import pso.mcpso.MCPSOAlgorithm;
import evolution.data.Problem;

public class MCPSOFrameController {
	private MCPSOFrame frame;
	private SlavesTableModel slavesTableModel = new SlavesTableModel();

	public void run() {
		frame = MCPSOFrame.getInstance();

		Problem[] problems = Problem.values();
		Arrays.sort(problems, new ProblemComparator());

		frame.getProblemComboBox().setModel(
				new DefaultComboBoxModel<>(problems));
		frame.getProblemComboBox().addActionListener(
				new SelectedProblemDimensionsListener());

		frame.getProblemDimensionsSpinner().setModel(
				new SpinnerNumberModel(20, 2, Integer.MAX_VALUE, 1));
		frame.getSwarmSizeSpinner().setModel(
				new PositiveIntegerSpinnerModel(40));
		frame.getEpochLengthSpinner().setModel(
				new SpinnerNumberModel(50, 1, null, 1));
		frame.getIterationsSpinner().setModel(
				new SpinnerNumberModel(1_000, 1, null, 1));

		frame.getInertiaSpinner().setModel(
				new SpinnerNumberModel(0.7, 0d, null, 0.01));
		frame.getBeginInertiaSpinner().setModel(
				new SpinnerNumberModel(0.7, 0d, null, 0.01));
		frame.getEndIntertiaSpinner().setModel(
				new SpinnerNumberModel(0.7, 0d, null, 0.01));

		frame.getC1FactorSpinner().setModel(
				new SpinnerNumberModel(2.05, 0d, null, 0.01));
		frame.getC2FactorSpinner().setModel(
				new SpinnerNumberModel(2.05, 0d, null, 0.01));
		frame.getC3FactorSpinner().setModel(
				new SpinnerNumberModel(2d, 0d, null, 0.01));

		JXTable slavesTable = frame.getSlavesTable();
		slavesTable.setModel(slavesTableModel);
		slavesTable.setDefaultEditor(PSO.class, new PSOTypeCellEditor());
		slavesTable.setSortOrderCycle(SortOrder.ASCENDING,
				SortOrder.DESCENDING, SortOrder.UNSORTED);
		slavesTable.packAll();

		RemoveSlaveAction removeSlaveAction = new RemoveSlaveAction();
		slavesTable.getSelectionModel().addListSelectionListener(
				removeSlaveAction);

		JButton okButton = frame.getOkButton();
		okButton.setAction(new RunAction());
		frame.getAddSlaveButton().setAction(new AddSlaveAction());
		frame.getRemoveSlaveButton().setAction(removeSlaveAction);
		frame.getRootPane().setDefaultButton(okButton);

		frame.pack();
		frame.setMinimumSize(frame.getPreferredSize());
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLocationByPlatform(true);
		frame.setVisible(true);
	}

	private final class SelectedProblemDimensionsListener implements
			ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			JSpinner dimensionsSpinner = frame.getProblemDimensionsSpinner();

			boolean is2D = String.valueOf(
					frame.getProblemComboBox().getSelectedItem()).endsWith(
					" (2D)");
			if (is2D) {
				dimensionsSpinner.setEnabled(false);
				dimensionsSpinner.setValue(2);
			} else {
				dimensionsSpinner.setEnabled(true);
			}
		}
	}

	private class ProblemComparator implements Comparator<Problem> {

		@Override
		public int compare(Problem o1, Problem o2) {
			return o1.toString().compareTo(o2.toString());
		}
	}

	public class RunAction extends AbstractAction {

		private static final long serialVersionUID = -7735114903682092080L;

		public RunAction() {
			super("Uruchom");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			final MCPSOAlgorithm mcpso = new MCSPSOBuilder().build(frame);

			ProgressDialogController progressDialogController = new ProgressDialogController();
			progressDialogController.run(mcpso);

			Executors.newSingleThreadExecutor().execute(new Runnable() {

				@Override
				public void run() {
					mcpso.run();
				}
			});
		}
	}

	public class AddSlaveAction extends AbstractAction {

		private static final long serialVersionUID = 416713868421000849L;

		public AddSlaveAction() {
			super("Dodaj");
		}

		@Override
		public void actionPerformed(ActionEvent arg0) {
			int lastRowIndex = slavesTableModel.appendRow();
			JTable slavesTable = frame.getSlavesTable();
			slavesTable.scrollRectToVisible(slavesTable.getCellRect(
					lastRowIndex, 0, true));
			slavesTable.getSelectionModel().setSelectionInterval(lastRowIndex,
					lastRowIndex);
		}
	}

	public class RemoveSlaveAction extends AbstractAction implements
			ListSelectionListener {

		private static final long serialVersionUID = -3348472716442228457L;

		public RemoveSlaveAction() {
			super("Usuń");
			setEnabled(false);
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			JTable slavesTable = frame.getSlavesTable();
			int[] selectedRows = slavesTable.getSelectedRows();

			int length = selectedRows.length;
			int[] modelRows = new int[length];
			for (int i = 0; i < length; i++) {
				modelRows[i] = slavesTable
						.convertRowIndexToModel(selectedRows[i]);
			}

			Arrays.sort(modelRows);
			for (int i = length - 1; i >= 0; i--) {
				slavesTableModel.removeRow(modelRows[i]);
			}
		}

		@Override
		public void valueChanged(ListSelectionEvent e) {
			if (!e.getValueIsAdjusting()) {
				setEnabled(frame.getSlavesTable().getSelectedRow() != -1);
			}
		}
	}
}
